/*
 * Copyright 2011 J. Auer, J. Andert, F.-J. Jendrusch, U. Celebi
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
package de.fuberlin.replication.application.android.communication;

import java.io.IOException;

import android.app.Dialog;
import android.app.ProgressDialog;
import android.content.DialogInterface;
import android.content.Intent;
import android.os.AsyncTask;
import android.os.Bundle;
import android.util.Log;
import android.widget.Toast;
import de.fuberlin.replication.R;
import de.fuberlin.replication.application.android.RotatableAbstract;

/**
 * Android-specific abstract communication helper.
 */
public abstract class CommunicationHelperAbstract extends RotatableAbstract implements CommunicationHelper {
	private static final String TAG = "CommunicationHelper";

	/** Synchronizing dialog id. */
	public static final int DIALOG_SYNCHRONIZING = 2;

	/** Initializing dialog id. */
	public static final int DIALOG_INITIALIZING = 3;

	/** Connecting dialog id. */
	public static final int DIALOG_CONNECTING = 4;

	/**
	 * Constructor.
	 */
	public CommunicationHelperAbstract() {
	}

	/**
	 * Starts listening.
	 * 
	 * @throws IOException on error.
	 */
	protected abstract void listen() throws IOException;

	/**
	 * Starts connecting.
	 * 
	 * @throws IOException on error.
	 */
	protected abstract void connect() throws IOException;

	/**
	 * Causes the current thread to wait until the communication is established, or
	 * {@link CommunicationHelperAbstract#close()} is called.
	 * 
	 * @throws InterruptedException may be thrown if any thread interrupted the current thread before or while the
	 *             current thread was waiting for the communication to be established.
	 */
	protected abstract void waitEstablished() throws InterruptedException;

	/**
	 * Closes any open connection attempt, listener and established connection.
	 * 
	 * @throws IOException on error.
	 */
	protected abstract void close() throws IOException;

	@Override
	public void onSynchronizationStart() {
		getUIHandler().obtainMessage(UICallback.MESSAGE_DIALOG_SHOW, DIALOG_SYNCHRONIZING, 0).sendToTarget();
		getUIHandler().obtainMessage(UICallback.MESSAGE_DIALOG_SHOW, DIALOG_INITIALIZING, 0).sendToTarget();

		try {
			try {
				listen();
			} catch (IOException e) {
				Log.e(TAG, "listen() failed", e);

				getUIHandler().obtainMessage(UICallback.MESSAGE_TOAST, R.string.toast_listen_failure,
						Toast.LENGTH_SHORT).sendToTarget();
			}

			try {
				waitEstablished();
			} catch (InterruptedException e) {
				throw new IllegalStateException();
			}
		} finally {
			getUIHandler().obtainMessage(UICallback.MESSAGE_DIALOG_REMOVE, DIALOG_INITIALIZING, 0).sendToTarget();
		}
	}

	@Override
	public void onSynchronizationException(Exception e) {
	}

	@Override
	public void onSynchronizationStop() {
		try {
			close();
		} catch (IOException e) {
			Log.e(TAG, "close() failed", e);
		} finally {
			getUIHandler().obtainMessage(UICallback.MESSAGE_DIALOG_REMOVE, DIALOG_SYNCHRONIZING, 0).sendToTarget();
		}
	}

	@Override
	public void onActivityResult(int requestCode, int resultCode, Intent data) {
	}

	@Override
	public Dialog onCreateDialog(int id, Bundle args) {
		ProgressDialog progress = new ProgressDialog(getActivity());

		switch (id) {
			case DIALOG_SYNCHRONIZING:
				progress.setTitle(getActivity().getText(R.string.dialog_synchronizing_title));
				progress.setMessage(getActivity().getText(R.string.dialog_synchronizing_message));
				progress.setIndeterminate(true);
				progress.setCancelable(false);

				break;
			case DIALOG_INITIALIZING:
				progress.setTitle(getActivity().getText(R.string.dialog_initializing_title));
				progress.setMessage(getActivity().getText(R.string.dialog_initializing_message));
				progress.setButton(DialogInterface.BUTTON_POSITIVE,
						getActivity().getText(R.string.dialog_initializing_button_connect),
							new DialogInterface.OnClickListener() {
								@Override
								public void onClick(DialogInterface dialog, int which) {
									try {
										connect();
									} catch (IOException e) {
										Log.e(TAG, "connect() failed", e);

										getUIHandler().obtainMessage(UICallback.MESSAGE_TOAST,
												R.string.toast_connect_failure, Toast.LENGTH_SHORT).sendToTarget();
									}
								}
							});
				progress.setIndeterminate(true);
				progress.setCancelable(true);
				progress.setOnCancelListener(new DialogInterface.OnCancelListener() {
					@Override
					public void onCancel(DialogInterface dialog) {
						try {
							close();
						} catch (IOException e) {
							Log.e(TAG, "close() failed", e);
						}
					}
				});

				break;
			case DIALOG_CONNECTING:
				progress.setTitle(getActivity().getText(R.string.dialog_connecting_title));
				progress.setMessage(getActivity().getText(R.string.dialog_connecting_message));
				progress.setIndeterminate(true);
				progress.setCancelable(false);

				break;
			default:
				return null;
		}

		return progress;
	}

	@Override
	public void onPrepareDialog(int id, Dialog dialog, Bundle args) {
	}

	/**
	 * Abstract asynchronous connect task.
	 */
	protected abstract class ConnectTask extends AsyncTask<String, Void, Boolean> {
		/**
		 * Constructor.
		 */
		public ConnectTask() {
		}

		@Override
		protected void onPreExecute() {
			getUIHandler().obtainMessage(UICallback.MESSAGE_DIALOG_SHOW, DIALOG_CONNECTING, 0).sendToTarget();
		}

		@Override
		protected void onPostExecute(Boolean result) {
			getUIHandler().obtainMessage(UICallback.MESSAGE_DIALOG_REMOVE, DIALOG_CONNECTING, 0).sendToTarget();

			if (!result.booleanValue()) {
				getUIHandler().obtainMessage(UICallback.MESSAGE_DIALOG_SHOW, DIALOG_INITIALIZING, 0).sendToTarget();
				getUIHandler().obtainMessage(UICallback.MESSAGE_TOAST, R.string.toast_connect_failure,
						Toast.LENGTH_SHORT).sendToTarget();
			}
		}
	}
}